﻿namespace EggOfPanKu.Main
{
    using System;
    using System.Collections.Generic;
    using System.Collections.ObjectModel;
    using IO = System.IO;
    using System.Linq;
    using System.ServiceModel;
    using System.Text;
    using System.Windows;
    using System.Windows.Controls;
    using System.Windows.Data;
    using System.Windows.Documents;
    using System.Windows.Input;
    using System.Windows.Media;
    using System.Windows.Media.Animation;
    using System.Windows.Media.Imaging;
    using System.Windows.Navigation;
    using System.Windows.Shapes;

    using EggOfPanKu.Library.Objects;
    using EggOfPanKu.Library.Settings;
    using EggOfPanKu.Main.Controls;
    using WebPaigeDesign.Utilities;
    using System.Diagnostics;
    using EggOfPanKu.Library.Infrastructure;

    /// <summary>
    /// Interaction logic for PlayWindow.xaml
    /// </summary>
    public partial class PlayWindow : Window
    {
        #region Fields

        private string _ImagePath;
        private Point _MyCanvasCenter;
        private GameDeck _MyDeck;
        private CardStack _MyFateHand;
        private CardControl _MyStronghold;
        private Point _TheirCanvasCenter;
        private GameDeck _TheirDeck;
        private CardControl _TheirStronghold;
        private ServiceHost Host;
        private NetworkLibrary.Common.Player currPlayer;
        private NetworkLibrary.Client.ProxySingleton proxySingleton = NetworkLibrary.Client.ProxySingleton.GetInstance();

        #endregion Fields

        #region Constructors

        /// <summary>
        /// Initializes a new instance of the <see cref="PlayWindow"/> class.
        /// </summary>
        public PlayWindow()
        {
            InitializeComponent();
            InitializeProperties();
        }

        #endregion Constructors

        #region Properties

        public string ImagePath
        {
            get { return _ImagePath; }
            set { _ImagePath = value; }
        }

        /// <summary>
        /// Gets the logger object from the Global App.
        /// </summary>
        /// <value>The log.</value>
        protected Logger Log
        {
            get
            {
                return App.Log;
            }
        }

        public Point MyCanvasCenter
        {
            get { return _MyCanvasCenter; }
            set { _MyCanvasCenter = value; }
        }

        public GameDeck MyDeck
        {
            get { return _MyDeck; }
            set { _MyDeck = value; }
        }

        public CardStack MyFateHand
        {
            get { return _MyFateHand; }
            set { _MyFateHand = value; }
        }

        public CardControl MyStronghold
        {
            get { return _MyStronghold; }
            set { _MyStronghold = value; }
        }

        public Point TheirCanvasCenter
        {
            get { return _TheirCanvasCenter; }
            set { _TheirCanvasCenter = value; }
        }

        public GameDeck TheirDeck
        {
            get { return _TheirDeck; }
            set { _TheirDeck = value; }
        }

        public CardControl TheirStronghold
        {
            get { return _TheirStronghold; }
            set { _TheirStronghold = value; }
        }

        #endregion Properties

        #region Methods
        /// <summary>
        /// Shows an error message and calls the <see cref="ProxySingleton">
        /// Proxy.AbortProxy()</see> method
        /// </summary>
        private void AbortProxyAndUpdateUI()
        {
            proxySingleton.AbortProxy();
            MessageBox.Show("An error occurred, Disconnecting");
        }

        public void CreateFocusStack_Click(object sender, EventArgs e)
        {

        }

        private void ConnectToHost()
        {
            //connect to proxy, and subscribe to its events
            proxySingleton.Connect(currPlayer);
            proxySingleton.ProxyEvent += new NetworkLibrary.Client.ProxySingleton.ProxyEventHandler(ProxySingleton_ProxyEvent);
            //one event subscription for this window
            proxySingleton.ProxyCallBackEvent += new NetworkLibrary.Client.ProxySingleton.ProxyCallBackEventHandler(this.ProxySingleton_ProxyCallBackEvent);
        }

        public void DrawStack_Click(object sender, EventArgs e)
        {

        }

        public void DrawXStack_Click(object sender, EventArgs e)
        {

        }

        /// <summary>
        /// Shows an error message and calls the <see cref="ProxySingleton">
        /// Proxy.ExitChatSession()</see> method
        /// </summary>
        /// <param name="errMessage">The error message to display</param>
        private void Error(string errMessage)
        {
            proxySingleton.ExitConnectSession();
            MessageBox.Show(errMessage, "Connection error", MessageBoxButton.OK, MessageBoxImage.Error);
        }

        /// <summary>
        /// Initializes the properties.
        /// </summary>
        public void InitializeProperties()
        {
            Log.Debug("Initializing Properties", "InitializeProperties");


            //Try to Load Card.XML from UserXmlPath Setting
            string cardXmlFile = ConfigManager.AppSettingsMgr.CardXmlPath;

            Log.Debug("Loading Cards XML File: {0}", "InitializeProperties", cardXmlFile);
            if (System.IO.File.Exists(cardXmlFile))
            {
                CardDatabase.LoadCardDatabase(cardXmlFile);
            }
            else
            {
                //Create Window and attach an event for Submitting a Card XML File
                LoadCards loadCardsWindow = new LoadCards();
                loadCardsWindow.SubmitCardFile += new LoadCards.CardFileSubmitHandler(loadCardsWindow_SubmitCardFile);

                loadCardsWindow.Show();
            }



            MyDeck = new GameDeck();
            MyStronghold = new CardControl();
            TheirStronghold = new CardControl();

            ImagePath = ConfigManager.AppSettingsMgr.CardImagePath;
            TxtYourName.Text = ConfigManager.UserSettingsMgr.UserNickName;
            Log.Debug("ImagePath:{0}\r\nYour NickName:{1}", "InitializeProperties", ImagePath, TxtYourName.Text);

            //TODO: Make this work
            //Initialize MyCanvas Zoom and Center
            //MyCanvasCenter = new Point(417.0, 335.0);
            //MyCanvasZoom = new ScaleTransform(1.60, 1.60);
        }

        void loadCardsWindow_SubmitCardFile(object sender, RoutedEventArgs e)
        {
            string cardXmlFile = ConfigManager.AppSettingsMgr.CardXmlPath;
            CardDatabase.LoadCardDatabase(cardXmlFile);
        }

        /// <summary>
        /// Handles the SubmitDeck event of the LoadDeckWindow control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.Windows.RoutedEventArgs"/> instance containing the event data.</param>
        public void LoadDeckWindow_SubmitDeck(object sender, RoutedEventArgs e)
        {
            //Load the Deck
            Log.Debug("Loading Game Deck File: {0}", "LoadDeckWindow_SubmitDeck", ((LoadDeck)sender).TxtDeckPath.Text);
            MyDeck = new GameDeck(((LoadDeck)sender).TxtDeckPath.Text);
        }

        public void LookAtBottomStack_Click(object sender, EventArgs e)
        {
        }

        public void LookTopStack_Click(object sender, EventArgs e)
        {
        }

        public void OpenStack_Click(object sender, EventArgs e)
        {
        }


        /// <summary>
        /// A delegate to allow a cross UI thread call to be marshalled to correct UI thread
        /// </summary>
        private delegate void ProxySingleton_ProxyEvent_Delegate(object sender, NetworkLibrary.Client.ProxyEventArgs e);

        /// <summary>
        /// This method checks to see if the current thread needs to be marshalled
        /// to the correct (UI owner) thread. If it does a new delegate is created
        /// which recalls this method on the correct thread
        /// </summary>
        /// <param name="sender"><see cref="Proxy_Singleton">ProxySingleton</see></param>
        /// <param name="e">The event args</param>
        private void ProxySingleton_ProxyEvent(object sender, NetworkLibrary.Client.ProxyEventArgs e)
        {
            if (!this.Dispatcher.CheckAccess())
            {
                this.Dispatcher.BeginInvoke(System.Windows.Threading.DispatcherPriority.Normal,
                new ProxySingleton_ProxyEvent_Delegate(ProxySingleton_ProxyEvent),
                sender, new object[] { e });
                return;
            }
            ////now marshalled to correct thread so proceed
            //foreach (Person person in e.list)
            //{
            //    lstChatters.Items.Add(person);
            //}

            //TODO:  Print out connected message somewhere
        }


        /// <summary>
        /// This method is called when ever the <see cref="Proxy_Singleton">
        /// Proxy</see> ProxyCallBackEvent is raised, in response to an incoming message
        /// When this method is called it will examine the events 
        /// ProxyCallBackEventArgs to see what type of message is being recieved, 
        /// and will then call the correspoding method. 
        /// <see cref="ChatProxy">UserEnter/UserLeave</see> are not dealt with 
        /// as they are not relevant to this control. They are dealt with by
        /// <see cref="Window1">the main WPF window</see>
        /// </summary>
        /// <param name="sender">the sender, which is not used</param>
        /// <param name="e">The ChatEventArgs</param>
        public void ProxySingleton_ProxyCallBackEvent(object sender, NetworkLibrary.Client.ProxyCallBackEventArgs e)
        {
            switch (e.callbackType)
            {
                case NetworkLibrary.Client.CallBackType.Receive:
                    Receive(e.player.Name, e.message);
                    break;
                case NetworkLibrary.Client.CallBackType.ReceiveWhisper:
                    ReceiveWhisper(e.player.Name, e.message);
                    break;
                case NetworkLibrary.Client.CallBackType.UserEnter:
                    break;
                case NetworkLibrary.Client.CallBackType.UserLeave:
                    break;

            }
        }

        public void Receive(string senderName, string message)
        {
            // AppendText(senderName + ": " + message + Environment.NewLine);
        }

        public void ReceiveWhisper(string senderName, string message)
        {
            // AppendText(senderName + " whisper: " + message + Environment.NewLine);
        }

        private void SayAndClear(string to, string msg, bool pvt)
        {
            if (msg != "")
            {
                try
                {
                    proxySingleton.SayAndClear(to, msg, pvt);
                    //TxtChatMsg.Text = "";
                }
                catch
                {
                    AbortProxyAndUpdateUI();
                    //AppendText("Disconnected at " + DateTime.Now.ToString() + Environment.NewLine);
                    Error("Error: Connection to chat server lost!");
                }
            }
        }

        public void SearchStack_Click(object sender, EventArgs e)
        {
        }

        public void ShuffleStack_Click(object sender, EventArgs e)
        {
        }

        //TODO: Find a Better way to do this?
        private static double GetRotationAngle(Card card)
        {
            double angle = 0.0;

            if (card.IsBowed)
            {
                angle = angle + 90;
            }
            if (card.IsDishonored)
            {
                angle = angle + 180;
            }

            return angle;
        }

        /// <summary>
        /// Adds the card to canvas.
        /// </summary>
        /// <param name="canvas">The canvas.</param>
        /// <param name="cardControl">The card control.</param>
        /// <param name="p">The p.</param>
        private void AddCardToCanvas(Canvas canvas, CardControl cardControl, Point p)
        {
            Log.Info("Adding Card \"{0}\" To Canvas \"{1}\"", "AddCardToCanvas", cardControl.Card.CardID, canvas.Name);
            //Initialize Card Control
            cardControl.ControlID = canvas.Children.Count + 1;
            cardControl.ViewCard += new CardControl.ViewCardHandler(ViewCard_Click);
            cardControl.BowCard += new CardControl.BowCardHandler(BowCard_Click);
            cardControl.DishonorCard += new CardControl.DishonorCardHandler(DishonorCard_Click);

            //Add new Control to Canvas
            canvas.Children.Add(cardControl);
            Canvas.SetLeft(cardControl, p.X);
            Canvas.SetTop(cardControl, p.Y);
        }

        /// <summary>
        /// Handles the Click event of the BowCard control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.Windows.RoutedEventArgs"/> instance containing the event data.</param>
        private void BowCard_Click(object sender, RoutedEventArgs e)
        {
            CardControl cardControl = sender as CardControl;
            Log.Info("Bowing Card \"{0}\" ", "BowCard_Click", cardControl.Card.CardID);
            int cardControlIndex = MyPlayCanvas.Children.IndexOf(cardControl);

            if (cardControl.Card.IsBowed)
            {

                MyPlayCanvas.Children[cardControlIndex].RenderTransform = new RotateTransform(GetRotationAngle(cardControl.Card), cardControl.CardImage.Width / 2, cardControl.CardImage.Height / 2);

            }
            else
            {
                MyPlayCanvas.Children[cardControlIndex].RenderTransform = new RotateTransform(GetRotationAngle(cardControl.Card), cardControl.CardImage.Width / 2, cardControl.CardImage.Height / 2);
            }
        }

        /// <summary>
        /// Handles the Click event of the DeckEditor control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.Windows.RoutedEventArgs"/> instance containing the event data.</param>
        private void DeckEditor_Click(object sender, RoutedEventArgs e)
        {
            //Start the Deck Editor in a new process
            Log.Info("Starting DeckBuilder", "DeckEditor_Click");
            Process p = new Process();
            p.StartInfo.FileName = IO.Path.Combine(ConfigManager.AppSettingsMgr.ExecutingAssemblyPath, ConfigManager.AppSettingsMgr.DeckEditorApplicationName + ".exe");
            p.Start();
        }

        /// <summary>
        /// Handles the Click event of the DishonorCard control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.Windows.RoutedEventArgs"/> instance containing the event data.</param>
        private void DishonorCard_Click(object sender, RoutedEventArgs e)
        {
            CardControl cardControl = sender as CardControl;
            Log.Info("Dishonoring Card \"{0}\" ", "DishonorCard_Click", cardControl.Card.CardID);
            int cardControlIndex = MyPlayCanvas.Children.IndexOf(cardControl);

            if (cardControl.Card.IsDishonored)
            {
                MyPlayCanvas.Children[cardControlIndex].RenderTransform = new RotateTransform(GetRotationAngle(cardControl.Card), cardControl.CardImage.Width / 2, cardControl.CardImage.Height / 2);
            }
            else
            {
                MyPlayCanvas.Children[cardControlIndex].RenderTransform = new RotateTransform(GetRotationAngle(cardControl.Card), cardControl.CardImage.Width / 2, cardControl.CardImage.Height / 2);
            }
        }

        /// <summary>
        /// Does the application shutdown.
        /// </summary>
        private void DoApplicationShutdown()
        {
            //TODO:  Check for active games, clean up, then shut down the application
            this.Close();
        }

        /// <summary>
        /// Handles the Click event of the ExitGame control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.Windows.RoutedEventArgs"/> instance containing the event data.</param>
        private void ExitGame_Click(object sender, RoutedEventArgs e)
        {
            DoApplicationShutdown();
        }

        /// <summary>
        /// Handles the Click event of the JoinGame control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.Windows.RoutedEventArgs"/> instance containing the event data.</param>
        private void JoinGame_Click(object sender, RoutedEventArgs e)
        {
            //Create Window and attach an event for Submitting a Deck
            LoadDeck loadDeckWindow = new LoadDeck();
            loadDeckWindow.SubmitDeck += new LoadDeck.DeckSubmitHandler(LoadDeckWindow_SubmitDeck);

            //Show the Window
            loadDeckWindow.Show();
        }

        /// <summary>
        /// Handles the MouseLeave event of the PlayCanvas control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.Windows.Input.MouseEventArgs"/> instance containing the event data.</param>
        private void PlayCanvas_MouseLeave(object sender, MouseEventArgs e)
        {
            ((EggOfPanKu.Main.Controls.DragCanvas)sender).isPanInProgress = false;
        }

        /// <summary>
        /// Handles the MouseMove event of the PlayCanvas control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.Windows.Input.MouseEventArgs"/> instance containing the event data.</param>
        private void PlayCanvas_MouseMove(object sender, MouseEventArgs e)
        {
            base.OnMouseMove(e);
            EggOfPanKu.Main.Controls.DragCanvas thisCanvas = ((EggOfPanKu.Main.Controls.DragCanvas)sender);

            Point thisCanvasCenter = new Point();
            ScaleTransform thisCanvasZoom = null;
            if (thisCanvas == MyPlayCanvas)
            {
                thisCanvasCenter = MyCanvasCenter;
                thisCanvasZoom = MyCanvasZoom;
            }
            else
            {
                thisCanvasCenter = TheirCanvasCenter;
                thisCanvasZoom = TheirCanvasZoom;
            }

            //Perform a Pan of the Canvas
            if (thisCanvas.isPanInProgress)
            {
                //Get the originalPoint based on the user's original left click
                Point originalPoint = new Point(thisCanvas.origCursorLocation.X, thisCanvas.origCursorLocation.Y);
                //Get the current mouse position relative to the DeckCanvasArea
                Point currentPoint = e.GetPosition(thisCanvas);

                double newX = thisCanvasCenter.X - (currentPoint.X - originalPoint.X) * .1;
                double newY = thisCanvasCenter.Y - (currentPoint.Y - originalPoint.Y) * .1;

                if (newX < 0)
                    newX = 0;
                if (newX > thisCanvas.ActualWidth)
                    newX = thisCanvas.ActualWidth;

                if (newY < 0)
                    newY = 0;
                if (newY > thisCanvas.ActualHeight)
                    newY = thisCanvas.ActualHeight;

                thisCanvasCenter = new Point(newX, newY);

                thisCanvasZoom.CenterX = newX;
                thisCanvasZoom.CenterY = newY;

                if (thisCanvas == MyPlayCanvas)
                {
                    MyCanvasCenter = thisCanvasCenter;
                    MyCanvasZoom = thisCanvasZoom;
                }
                else
                {
                    TheirCanvasCenter = thisCanvasCenter;
                    TheirCanvasZoom = thisCanvasZoom;
                }
            }
        }

        /// <summary>
        /// Handles the MouseWheel event of the PlayCanvas control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.Windows.Input.MouseWheelEventArgs"/> instance containing the event data.</param>
        private void PlayCanvas_MouseWheel(object sender, MouseWheelEventArgs e)
        {
            //Initialize Canvas values
            Canvas thisCanvas = ((Canvas)sender);
            ScaleTransform thisCanvasZoom = null;
            if (thisCanvas == MyPlayCanvas)
            {
                thisCanvasZoom = MyCanvasZoom;
            }
            else
            {
                thisCanvasZoom = TheirCanvasZoom;
            }

            //Perform rescale of canvas
            double scaleFactor = e.Delta * .001;
            double newScale = thisCanvasZoom.ScaleX + scaleFactor;
            if (newScale < 1)
            {
                thisCanvasZoom.ScaleX = 1;
                thisCanvasZoom.ScaleY = 1;
                return;
            }
            if (newScale > 7)
            {
                thisCanvasZoom.ScaleX = 7;
                thisCanvasZoom.ScaleY = 7;
                return;
            }
            thisCanvasZoom.ScaleX = newScale;
            thisCanvasZoom.ScaleY = newScale;

            //Set Canvas values
            if (thisCanvas == MyPlayCanvas)
            {
                MyCanvasZoom = thisCanvasZoom;
            }
            else
            {
                TheirCanvasZoom = thisCanvasZoom;
            }
        }

        /// <summary>
        /// Previews the card.
        /// </summary>
        /// <param name="card">The card.</param>
        private void PreviewCard(Card card)
        {
            //PreviewWindow pw = new PreviewWindow();
            //pw.LoadCard(card, ImagePath);
            //pw.Show();
        }

        /// <summary>
        /// Handles the Click event of the StartGame control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.Windows.RoutedEventArgs"/> instance containing the event data.</param>
        private void StartGame_Click(object sender, RoutedEventArgs e)
        {
            //Verify their is a player present
            Log.Info("Starting a game", "StartGame_Click");
            //Shuffle card stacks
            MyDeck.FateDeck.ShuffleCards();
            MyDeck.DynastyDeck.ShuffleCards();

            Log.Info("drawing four dynasty cards and setting up provinces", "StartGame_Click");
            //Layout Dynasty Cards
            List<Card> provinces = MyDeck.DynastyDeck.DrawCards(4);
            for (int i = 0; i < provinces.Count; i++)
            {
                CardControl provinceCard = new CardControl(provinces[i]);
                provinceCard.FlipCard();
                AddCardToCanvas(MyPlayCanvas, provinceCard, new Point(380 + i * 50, 255));
            }

            Log.Info("Creating Fate hand", "StartGame_Click");
            //Create FateHand
            CardStack fateHand = new CardStack(MyDeck.FateDeck.DrawCards(ConfigManager.AppSettingsMgr.CardsToDraw), CardStackType.FateInHand);
            ObservableCollection<CardInStack> cardStack = new ObservableCollection<CardInStack>();
            foreach (Card card in fateHand.Cards)
            {
                CardInStack newCard = new CardInStack(card, ImagePath);
                cardStack.Add(newCard);
            }
            StackWindow handWindow = new StackWindow(cardStack, Orientation.Horizontal);
            handWindow.Show();

            Log.Info("Setting out Stronghold with{0} Border Keep and Bamboo Harvesters", "StartGame_Click", (ConfigManager.AppSettingsMgr.UseCEHoldings ? "" : "out"));
            //Layout Stronghold, BorderKeep, Bamboo Harvester (conditional?)
            MyStronghold = new CardControl(MyDeck.StrongholdCard);
            AddCardToCanvas(MyPlayCanvas, MyStronghold, new Point(275, 255));

            if (ConfigManager.AppSettingsMgr.UseCEHoldings)
            {
                Log.Info("Adding CE Holdings to the Table", "StartGame_Click");
                // add Border Keep and possibly Bamboo Harvesters here
            }

        }

        /// <summary>
        /// Handles the Click event of the ViewCard control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.Windows.RoutedEventArgs"/> instance containing the event data.</param>
        private void ViewCard_Click(object sender, RoutedEventArgs e)
        {
            CardControl cardControl = sender as CardControl;
            PreviewCard(cardControl.Card);
        }

        #endregion Methods

        private void HostTable_Click(object sender, RoutedEventArgs e)
        {
            try
            {
                Uri uri = new Uri(ConfigManager.AppSettingsMgr.ConnectionAddress);
                Host = new ServiceHost(typeof(NetworkLibrary.Host.ConnectService), uri);
                Host.Open();

                currPlayer = new NetworkLibrary.Common.Player(TxtYourName.Text);

                ConnectToHost();
            }
            catch (System.Exception exc)
            {
                string error = exc.Message;
                Host.Abort();
                Host.Close();
            }
        }

        private void ConnectTable_Click(object sender, RoutedEventArgs e)
        {
            currPlayer = new NetworkLibrary.Common.Player(TxtYourName.Text);
            ConnectToHost();
        }
    }
}